﻿using System;
using System.Collections.Generic;
using System.Messaging;
using System.Text.RegularExpressions;
using System.ComponentModel;
using DbMon.NET;

namespace BTS.Testing.TraceMonitor
{
    /// <summary>
        /// Wrappers the event trace management for the testing utilities
        /// </summary>
        public class EventTraceManager
        {
            private static string _loggerName;
            private static BackgroundWorker _backgroundWorker = new BackgroundWorker();
            private static string _eventQueuePath = string.Empty;
            private static readonly List<string> ProviderGuidsToMonitor = new List<string>();

            /// <summary>
            /// Starts the monitor
            /// </summary>
            /// <param name="loggerName"></param>            
            /// <param name="eventQueuePath"></param>            
            public static void Start(string loggerName, string eventQueuePath)
            {
                _loggerName = loggerName;

                //MSMQ Setup
                _eventQueuePath = eventQueuePath;
                if (MessageQueue.Exists(_eventQueuePath))
                {
                    var queue = new MessageQueue(_eventQueuePath);
                    queue.Purge();
                }
                else                
                    MessageQueue.Create(_eventQueuePath);
                
                //Setup CAT UI Framework To Turn on trace and format events
                ProviderGuidsToMonitor.Add("5CBD8BA0-60F8-401b-8FF5-C7F3D5FABE41");//Business Activity Tracking Components
                ProviderGuidsToMonitor.Add("78E2D466-590F-4991-9287-3F00BA62793D");//Business Rules Components
                ProviderGuidsToMonitor.Add("2E5D65D8-71F9-43e9-B477-733EF6212895");//Data Access Components
                ProviderGuidsToMonitor.Add("D2316AFB-414B-42e4-BB7F-3AA92B96A98A");//Orchestration/Workflow Components
                ProviderGuidsToMonitor.Add("691CB4CB-D20C-408e-8CFF-FD8A01CD2F75");//Pipeline Components
                ProviderGuidsToMonitor.Add("226445A8-5AF3-4dbe-86D2-73E9B965378E");//Transform Components
                ProviderGuidsToMonitor.Add("E67E8346-90F1-408b-AF40-222B6E3C5ED6");//Web/WCF Services
                ProviderGuidsToMonitor.Add("6A223DEA-F806-4523-BAD0-312DCC4F63F9");//Custom Components
                                
                BizTalkCatInstrumentationController.TraceManager.StartTrace(IntPtr.Zero, loggerName, "All", true, false, ProviderGuidsToMonitor, false);

                //Setup DBMon for monitoring the trace/debug
                _backgroundWorker = new BackgroundWorker {WorkerSupportsCancellation = true};
                _backgroundWorker.DoWork += BackgroundWorkerStart;
                _backgroundWorker.RunWorkerAsync();
            }
            /// <summary>
            /// Stops the monitor
            /// </summary>
            public static void Stop()
            {
                //Stop CAT Framework UI
                BizTalkCatInstrumentationController.TraceManager.StopTrace(IntPtr.Zero, _loggerName, true, false, false);

                //Stop backgroup worker DBMon                
                DebugMonitor.Stop();
                _backgroundWorker.CancelAsync();
                _backgroundWorker = null;
                
                //Clear Queue
                var queue = new MessageQueue(_eventQueuePath);
                queue.Purge();
            }

            public static void BackgroundWorkerStart(object sender, DoWorkEventArgs e)
            {
                DebugMonitor.OnOutputDebugString += OnOutputDebugString;
                DebugMonitor.Start(); 
            }

            public static void BackgroundWorkerCancel(object sender, CancelEventArgs e)
            {
                DebugMonitor.Stop();
            }
           

            private static void OnOutputDebugString(int pid, string text)
            {           
                Console.ResetColor();
                if (Regex.IsMatch(text, Config.FilterRegEx))
                {
                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                    Console.WriteLine(string.Format("Debug Message: {0}", text));
                    var message = new Message(text, new ActiveXMessageFormatter())
                                  {
                                      Label = text.Length > 100 ? text.Substring(0, 100) : text,
                                      Recoverable = true
                                  };

                    var queue = new MessageQueue(_eventQueuePath);
                    queue.Send(message);
                }
                else
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(string.Format("Debug Message: {0}", text));
                }
            }


        /// <summary>
        /// Waits for the next message on the queue and checks if it meets the regex in the label or optionally the body
        /// </summary>
        /// <param name="regEx"></param>
        /// <param name="includeBodyCompare"></param>
        /// <param name="timeoutSeconds" />
        /// <param name="deleteAfterFound"></param>
        /// <returns></returns>
        public static bool PeekForNextEvent(string regEx, bool includeBodyCompare, int timeoutSeconds, bool deleteAfterFound)
            {
                var found = false;
                try
                {
                    var queue = new MessageQueue(_eventQueuePath);
                    var msg = queue.Peek(new TimeSpan(0, 0, timeoutSeconds));
                    if (msg != null)
                    {
                        if (Regex.IsMatch(msg.Label, regEx, RegexOptions.IgnoreCase))
                        {
                            Console.WriteLine(string.Format("Matching label: {0}", msg.Label));
                            found = true;
                        }

                        if (!found && includeBodyCompare)
                        {
                            msg.Formatter = new ActiveXMessageFormatter();
                            var body = msg.Body as string;
                            if (body != null)
                                if (Regex.IsMatch(body, regEx, RegexOptions.IgnoreCase | RegexOptions.Multiline))
                                {
                                    Console.WriteLine(string.Format("Matching body: {0}", msg.Label));
                                    found = true;
                                }
                        }

                        if (found && deleteAfterFound)
                            queue.ReceiveById(msg.Id); //Remove from queue
                    }

                    return found;
                }
                catch (MessageQueueException msmqEx)
                {
                    if (msmqEx.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
                        return false;
                    throw;
                }
            }

        /// <summary>
        /// Waits for the next message on the queue and checks if it meets the regex in the label or optionally the body
        /// </summary>
        /// <param name="regEx"></param>
        /// <param name="includeBodyCompare"></param>
        /// <param name="timeoutSeconds"></param>
        /// <param name="deleteAfterFound"></param>
        /// <returns></returns>
        public static bool PeekForAnyEvent(string regEx, bool includeBodyCompare, int timeoutSeconds, bool deleteAfterFound)
            {
                var found = false;
                var timeout = DateTime.Now.AddSeconds(timeoutSeconds);

                var queue = new MessageQueue(_eventQueuePath);
                var e = queue.GetMessageEnumerator2();
                while (true)
                {
                    while (e.MoveNext())
                    {
                        Message msg = e.Current;
                        if (msg != null)
                        {
                            if (Regex.IsMatch(msg.Label, regEx, RegexOptions.IgnoreCase))
                            {
                                Console.WriteLine(string.Format("Matching label: {0}", msg.Label));
                                found = true;
                            }

                            if (!found && includeBodyCompare)
                            {
                                msg.Formatter = new ActiveXMessageFormatter();
                                var body = msg.Body as string;
                                if (body != null)
                                    if (Regex.IsMatch(body, regEx, RegexOptions.IgnoreCase | RegexOptions.Multiline))
                                    {
                                        Console.WriteLine(string.Format("Matching Body: {0}", msg.Label));
                                        found = true;
                                    }
                            }

                            if (found && deleteAfterFound)
                                queue.ReceiveById(msg.Id);
                        }

                        if (found)
                            break;
                    }
                    if (found
                        || DateTime.Now > timeout)
                        break;
                }
                return found;
            }
        }
    }


